#include <my_hander.h>

typedef struct node
{
    int client_fd;
    time_t last_speech_time;
    char name_buf[20];
    struct node *last;
    struct node *next;
} Node;

typedef struct list
{
    Node *head;
    Node *tail;
    int List_length;
} List;

// 尾插法
void tail_insert(List* L, Node*node)
{
    if(L->List_length == 0)
    {
        L->head = node;
        L->tail = node;
        L->head->next = NULL;
        L->head->last = NULL;
    }
    else
    {
        L->tail->next = node;
        node->last = L->tail;
        L->tail = node;
        node->next = NULL;
    }
    L->List_length++;
    
}

//定位删除
void delete(List *L,Node *node)
{
    if(node == L->head)
    {
        // 删除的是头结点
        if (node->next == NULL)
        {
            L->head = NULL;
        }
        else
        {
            L->head = node->next;
            node->next->last = NULL;
        }
    
        L->List_length--;
        return;
    }
    if(node == L->tail)
    {
        L->tail = node->last;
        L->tail->next = NULL;
        L->List_length--;
        return;
    }
    node->last->next = node->next;
    node->next->last = node->last;
    L->List_length--;
}



int main()
{
    int server_fd = socket(AF_INET, SOCK_STREAM, 0);

    struct sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_port = htons(30003);
    server.sin_addr.s_addr= inet_addr("127.0.0.1");

    int reuse = 1;
	// SO_REUSEADDR是个布尔类型，定义一个int表示将其改为1
    setsockopt(server_fd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse));

    int ret = bind(server_fd, (struct sockaddr *)&server, sizeof(server));
    ERROR_CHECK(ret, -1, "bind");
    printf("端口号和IP与服务器绑定完成!\n");
  

    listen(server_fd, 50);
    printf("已转换为监听模式\n");

    printf("服务器已开启，关闭服务器请输入ctrl + d\n");

    // 初始化监听集合
    fd_set monitor_set;
    fd_set ready_set;
    FD_ZERO(&monitor_set);
    FD_ZERO(&ready_set);
    FD_SET(server_fd, &monitor_set);
    FD_SET(STDIN_FILENO, &monitor_set);

    // 初始化用户链表
    List *L = (List *)malloc(sizeof(List));
    L->List_length = 0;
    L->head = NULL;
    L->tail = NULL;

    time_t now_time;

    // 用于接受用户消息
    char message_buf[4096];

    // 用于检测用户命令
    char look_online[] = ".r";
    char pchat[] = "pchat";
    int pchat_len = strlen(pchat);
    char pchat_check[pchat_len];

    while(1)
    {
        memcpy(&ready_set, &monitor_set, sizeof(fd_set));

        struct timeval time_val;
        time_val.tv_sec = 1;
        time_val.tv_usec = 0;

        select(1024, &ready_set, NULL,NULL, &time_val);
        now_time = time(NULL);

        // 每两分钟广播一次当前在线人数
        if(now_time%120==0)
        {
            char broad_buf[100];
            sprintf(broad_buf, "当前有 %d 位成员在线！", L->List_length);
            Node *broadcast_node = (Node *)malloc(sizeof(Node));
            broadcast_node = L->head;
            while(broadcast_node!=NULL)
            {
                send(broadcast_node->client_fd, broad_buf, strlen(broad_buf), 0);
                broadcast_node = broadcast_node->next;
            }
        }

        // 长时间未发言踢出服务器
        if(L->List_length != 0)
        {
            Node *check_node;
            check_node = L->head;
            while(check_node!=NULL)
            {
                if((now_time - check_node->last_speech_time) > 300)
                {
                    Node *delete_node;
                    delete_node = check_node;
                    check_node = check_node->next;

                    char byebye_buf[] = "检测到您长时间未发言，已将您移出聊天";
                    send(delete_node->client_fd, byebye_buf, sizeof(byebye_buf), 0);
                    FD_CLR(delete_node->client_fd, &monitor_set);
                    delete(L, delete_node);
                    close(delete_node->client_fd);
                    
                    free(delete_node);
                    continue;
                }   
                
                check_node = check_node->next;
            }
                
            
        }

        // 有新用户到来
        if(FD_ISSET(server_fd, &ready_set))
        {
            Node *new_node = (Node *)malloc(sizeof(Node));

            // 设置新用户的文件描述符
            new_node->client_fd = accept(server_fd, NULL, NULL);

            // 保存新用户的昵称
            int name_len;
            recv(new_node->client_fd, &name_len, sizeof(name_len), 0);
            read(new_node->client_fd, new_node->name_buf, name_len);
            new_node->name_buf[name_len] = '\0';

            // 保存新用户的最后发言时间
            new_node->last_speech_time = time(NULL);
            printf("用户%s已连接\n", new_node->name_buf);
            tail_insert(L, new_node);
            // 将新用户加到监听集合中
            FD_SET(new_node->client_fd, &monitor_set);
        }


        // 遍历监听集合
        Node *user_node = (Node *)malloc(sizeof(Node));
        user_node = L->head;
        while(user_node!=NULL)
        {
            if(FD_ISSET(user_node->client_fd,&ready_set))
            {
                bzero(message_buf,sizeof(message_buf));
                int ret = recv(user_node->client_fd, message_buf, sizeof(message_buf), 0);
                if (ret == 0)
                {
                    Node *delete_node;
                    delete_node = user_node;
                    user_node = user_node->next;

                    /* Node *go = (Node *)malloc(sizeof(Node));
                     go = L->head;
                     char buf[2048];
                     bzero(buf,sizeof(buf));
                     sprintf(buf,"用户%s已断开链接！现在在线的用户有：\n", delete_node->name_buf);
                    while(go!=NULL)
                    {
                        if(go!=user_node)
                        {
                            char buf2[50];
                            sprintf(buf2, "用户名：%s,序列号为：%d\n", go->name_buf, go->client_fd);
                            int len1 = strlen(buf2);
                            strncat(buf, buf2, len1);
                        }
                        go = go->next;
                        
                    }
                    
                    Node *broadcast_node = (Node *)malloc(sizeof(Node));
                    broadcast_node = L->head;
                    while(broadcast_node!=NULL)
                    {
                        if(broadcast_node!=user_node)
                        {
                            send(broadcast_node->client_fd, buf, strlen(buf), 0);
                        }
                        broadcast_node = broadcast_node->next;
                     }*/

                    FD_CLR(delete_node->client_fd, &monitor_set);
                    close(delete_node->client_fd);
                    delete(L, delete_node);
                    free(delete_node);
                    continue;
                }
                user_node->last_speech_time = time(NULL);

                // .r 查看在线列表
                if(strcmp(look_online,message_buf)==0)
                {
                    Node *go = (Node *)malloc(sizeof(Node));
                    go = L->head;
                    char buf[2048];
                    bzero(buf,sizeof(buf));
                    sprintf(buf,"现在在线的用户有：\n");
                    while(go!=NULL)
                    {
                        if(go!=user_node)
                        {
                            char buf2[50];
                            sprintf(buf2, "用户名：%s,序列号为：%d\n", go->name_buf, go->client_fd);
                            int len1 = strlen(buf2);
                            strncat(buf, buf2, len1);
                        }
                        go = go->next;
                        
                    }
                    send(user_node->client_fd, buf, strlen(buf), 0);
                    user_node = user_node->next;
                    continue;
                }

                // pchat 私聊
                bzero(pchat_check,sizeof(pchat_check));
                strncpy(pchat_check, message_buf, pchat_len);
                pchat_check[pchat_len] = '\0';
                if (strcmp(pchat_check, pchat) == 0)
                {
                    char pchat_user;
                    pchat_user = message_buf[pchat_len];
                    int num = pchat_user - '0';
                    int message_len = strlen(message_buf);
                    
                    memmove(message_buf, message_buf + pchat_len + 1, message_len - pchat_len);

                    char all_buf[5120];
                    char buf[100];
                    sprintf(buf, "用户：%s对您私聊：\n", user_node->name_buf);
                    sprintf(all_buf, "%s%s",buf,message_buf);

                    send(num, all_buf, strlen(all_buf), 0);

                    user_node = user_node->next;

                    continue;
                }

                // 群聊发言
                int name_len = strlen(user_node->name_buf);
                char new_message_buf[5014];
                bzero(new_message_buf, sizeof(new_message_buf));
                strncat(new_message_buf, user_node->name_buf, name_len);
                int len = strlen(new_message_buf);
                new_message_buf[len] = ':';
                int message_len = strlen(message_buf);
                strncat(new_message_buf, message_buf,message_len);

                Node *broadcast_node = (Node *)malloc(sizeof(Node));
                broadcast_node = L->head;
                for (int j = 0;j < L->List_length;j++)
                {
                    if(broadcast_node != user_node)
                    {
                        send(broadcast_node->client_fd, new_message_buf, strlen(new_message_buf), 0);
                    }
                    if(broadcast_node->next != NULL)
                    {
                        broadcast_node = broadcast_node->next;
                    }
                }
            }
           
            user_node = user_node->next;
            
        }
        if(FD_ISSET(STDIN_FILENO,&ready_set))
        {
            char buf[20];
            int ret = read(STDIN_FILENO, buf, sizeof(buf));
            if(ret==0)
            {
                printf("关闭服务器！\n");
                Node *node;
                node = L->head;
                while(node!=NULL)
                {
                    FD_CLR(node->client_fd, &monitor_set);
                    close(node->client_fd);
                    node = node->next;
                }
                break;
            }
        }
    }

    close(server_fd);

    return 0;
}
